package com.zmn.brs.starter.support.aop;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.zmn.brs.beans.OperateRecordOps;
import com.zmn.brs.common.enums.EventEnum;
import com.zmn.brs.common.enums.PlatEnum;
import com.zmn.brs.common.model.mq.OperateRecordMq;
import com.zmn.brs.services.interfaces.IOperateRecordService;
import com.zmn.brs.starter.support.parse.OperateRecordValueParser;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 类描述：拦截器
 *
 * @author zhangyao
 * @since 2021/04/21 19:02
 */
@Slf4j
public class OperateRecordInterceptor extends OperateRecordValueParser implements InitializingBean, MethodInterceptor, Serializable {

    private OperateRecordOperationSource operateRecordOperationSource;

    private IOperateRecordService operateRecordService;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Method method = invocation.getMethod();
        return execute(invocation, invocation.getThis(), method, invocation.getArguments());
    }

    private Object execute(MethodInvocation invoker, Object target, Method method, Object[] args) throws Throwable {

        Class<?> targetClass = getTargetClass(target);
        Object ret = null;
        boolean success = true;
        String errorMsg = "";
        Throwable throwable = null;
        try {
            ret = invoker.proceed();
        } catch (Exception e) {
            success = false;
            errorMsg = e.getMessage();
            throwable = e;
        }
        try {
            Collection<OperateRecordOps> operations = getOperateRecordOperationSource().computeOperateRecordOperations(method, targetClass);
            if (!CollectionUtils.isEmpty(operations)) {
                recordExecute(ret, method, args, operations, targetClass, success, errorMsg);
            }
        } catch (Exception t) {
            //记录日志错误不要影响业务
            log.error("log record parse exception", t);
        }
        if (throwable != null) {
            throw throwable;
        }
        return ret;
    }

    private void recordExecute(Object ret, Method method, Object[] args, Collection<OperateRecordOps> operations,
                               Class<?> targetClass, boolean success, String errorMsg) {

        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
        for (OperateRecordOps operation : operations) {
            try {
                String objectId = operation.getObjectId();
                String objectIp = operation.getObjectIp();
                String objectIdentity = operation.getObjectIdentity();
                String objectMobile = operation.getObjectMobile();
                String plat = operation.getPlat();
                String channelId = operation.getChannelId();
                String unless = operation.getUnless();
                Assert.isTrue(!StringUtils.isEmpty(objectId), "对象ID为空");
                Assert.isTrue(!StringUtils.isEmpty(objectIdentity), "对象身份为空");
                //获取需要解析的表达式
                List<String> spElTemplates = Lists.newArrayList(objectId, objectIp, objectIdentity, objectMobile, plat, channelId, unless);
                Map<String, String> expressionValues = processTemplate(spElTemplates, ret, targetClass, method, args, errorMsg);
                String expressionString = expressionValues.get(unless);
                Boolean satisfy;
                if (!StringUtils.isEmpty(expressionString)) {
                    satisfy = parser.parseExpression(expressionString).getValue(context, Boolean.class);
                    log.info("操作记录注解EL表达式：{}，执行结果：{}", expressionString, satisfy);
                } else {
                    satisfy = true;
                }
                EventEnum event = operation.getEvent();
                if (!StringUtils.isEmpty(expressionValues.get(plat))) {
                    event = EventEnum.transformEventEnum(event, Integer.parseInt(expressionValues.get(plat)));
                }
                PlatEnum platEnum = StringUtils.isEmpty(expressionValues.get(plat)) ? null : PlatEnum.getPlatEnum(Integer.parseInt(expressionValues.get(plat)));
                Integer channelIdInt = StringUtils.isEmpty(expressionValues.get(channelId)) ? null : Integer.parseInt(expressionValues.get(channelId));
                OperateRecordMq recordModel = OperateRecordMq.builder()
                        .plat(platEnum)
                        .channelId(channelIdInt)
                        .objectId(Long.parseLong(expressionValues.get(objectId)))
                        .objectIdentity(Integer.parseInt(expressionValues.get(objectIdentity)))
                        .objectMobile(expressionValues.get(objectMobile))
                        .event(event)
                        .objectIp(expressionValues.get(objectIp))
                        .createTime(new Date())
                        .build();
                if (satisfy != null && satisfy) {
                    //save record 需要新开事务，失败日志不能因为事务回滚而丢失
                    Preconditions.checkNotNull(operateRecordService, "operateRecordService not init!!");
                    operateRecordService.record(recordModel);
                }
            } catch (Exception t) {
                log.error("log record execute exception", t);
            }
        }
    }

    private Class<?> getTargetClass(Object target) {
        return AopProxyUtils.ultimateTargetClass(target);
    }

    public OperateRecordOperationSource getOperateRecordOperationSource() {

        return operateRecordOperationSource;
    }

    public void setOperateRecordOperationSource(OperateRecordOperationSource operateRecordOperationSource) {

        this.operateRecordOperationSource = operateRecordOperationSource;
    }

    public void setOperateRecordService(IOperateRecordService operateRecordService) {

        this.operateRecordService = operateRecordService;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        operateRecordService = beanFactory.getBean(IOperateRecordService.class);
        Preconditions.checkNotNull(operateRecordService, "operateRecordService not null");
    }

}
